package testApplets;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;

import controlP5.ControlP5;
import controlP5.RadioButton;
import processing.core.PApplet;
import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.HasPos3D;
import simpleGeom.Mesh;
import simpleGeom.MeshEdge;
import simpleGeom.MeshFace;
import simpleGeom.MeshNode;
import simpleGeom.Node;
import simpleGeom.Plane;
import simpleGeom.SimpleObjLoaderStream;
import simpleGeom.SimpleTriangle;
import simpleGeom.Vec3D;

public class CropApplet extends PApplet {
	Bounds3D realBounds;
	Box3D b3d;
	ArrayList<Node> nodes = new ArrayList<Node>();
	ArrayList<SimpleTriangle> faces = new ArrayList<SimpleTriangle>();
	String folderPath = "/Users/caad/Desktop/Archive/";

	String pathObjIn = folderPath + "skinTestVersion1gRight.obj";

	ControlP5 cp5;

	float rotY;
	boolean flagRefresh;
	boolean flagCrop;
	boolean flagSave;

	Vector<SimpleTriangle> borderFaces;
	public int minX = 21;// 26
	public int maxX = -2;// 5
	public int minY = 17;// 20
	public int maxY = 80;// 72
	public int minZ = 18;// 28
	public int maxZ = 0;// 28
	//21_17_80_18
	public void setup() {
		size(1300, 750, P3D);
		realBounds = new Bounds3D();
		loadData(pathObjIn);
		b3d=realBounds;
		flagRefresh=true;
		createGUI();
	}

	public void setBounds() {
		b3d=new Box3D();
		b3d.x1 = realBounds.x1 + minX / 100f;
		b3d.x2 = realBounds.x2 - maxX / 100f; // 0.05F
		b3d.z1 = realBounds.z1 + minZ / 100f;// 0.5
		b3d.z2 = realBounds.z2 - maxZ / 100f;// 0.5f
		b3d.y1 = realBounds.y1 + minY / 100f;// 0.45
		b3d.y2 = realBounds.y2 - maxY / 100f;
	}
	public void cropToBounds(Box3D box){
		
		Plane plane=new Plane(new Vec3D(box.getX1(),0,0),new Vec3D(1,0,0));
		crop(plane);
		plane=new Plane(new Vec3D(box.getX2(),0,0),new Vec3D(-1,0,0));
		crop(plane);
		
		plane=new Plane(new Vec3D(0,box.getY1(),0),new Vec3D(0,1,0));
		crop(plane);
		plane=new Plane(new Vec3D(0,box.getY2(),0),new Vec3D(0,-1,0));
		crop(plane);
		
		plane=new Plane(new Vec3D(0,0,box.getZ1()),new Vec3D(0,0,1));
		crop(plane);
		plane=new Plane(new Vec3D(0,0,box.getZ2()),new Vec3D(0,0,-1));
		crop(plane);
		
	}
	
	public void crop(Plane plane) {
		ArrayList<SimpleTriangle> newTriangles = new ArrayList<SimpleTriangle>();
		for (int i = 0; i < faces.size(); i++) {
			if (i%100000==0){
				println(i*100f/faces.size());
			}
			SimpleTriangle f = faces.get(i);
			int nInside = 0;
			boolean[]inside=new boolean[3];
			for (int j = 0; j < f.getNodesNum(); j++) {

				HasPos3D cN = f.getNode(j);
				if (plane.getDistanceSigned(cN) >= 0) {
					nInside++;
					inside[j]=true;
				}
				else{
					inside[j]=false;
				}
			}
			if (nInside == 3) {
				// inside
				newTriangles.add(f);
			} else if (nInside > 0) {
				//intersects
				//println("inside");
				ArrayList<HasPos3D> nodes = new ArrayList<HasPos3D>();
				
				for (int j = 0; j < f.getNodesNum(); j++) {
					HasPos3D n1 = f.getNode(i);
					if (plane.getDistanceSigned(n1) >= 0) {
						nodes.add(n1);
					}
					int j2=(i + 1) % f.getNodesNum();
					HasPos3D n2 = f.getNode(j2);
					if (inside[j]!=inside[j2]){
					Vec3D newV1 = plane.intersectWithRay(n1, n2);
					//if (newV1 != null) {
						nodes.add(newV1);
					//}
					}
				}
				if (nodes.size() == 3) {
					newTriangles.add(new SimpleTriangle(nodes.get(0),
							nodes.get(1), nodes.get(2)));
				} else if (nodes.size() == 4) {
					newTriangles.add(new SimpleTriangle(nodes.get(0),
							nodes.get(1), nodes.get(2)));
					newTriangles.add(new SimpleTriangle(nodes.get(2),
							nodes.get(3), nodes.get(0)));
				}
			}
		}
		faces=newTriangles;
	}

	public void loadData(String pathObjIn) {
		SimpleObjLoaderStream stream = new SimpleObjLoaderStream();
		stream.init(pathObjIn);
		float[] vertex = stream.readVertex();
		while (vertex != null) {
			nodes.add(new Node(vertex[0], vertex[1], vertex[2]));
			vertex = stream.readVertex();
		}
		println("nodes: " + nodes.size());
		stream.close();
		stream.init(pathObjIn);
		int[] face = stream.readFace();
		while (face != null) {
			SimpleTriangle f = new SimpleTriangle(null, null, null);
			if (face.length == 4) {
				f = new SimpleTriangle(nodes.get(face[0] - 1),
						nodes.get(face[1] - 1), nodes.get(face[2] - 1));
				faces.add(f);
				f = new SimpleTriangle(nodes.get(face[2] - 1),
						nodes.get(face[3] - 1), nodes.get(face[0] - 1));
				faces.add(f);

				for (int i = 0; i < face.length; i++) {
					Node n = nodes.get(face[i] - 1);
					n.setComment(1);
				}
			} else {
				f = new SimpleTriangle(nodes.get(face[0] - 1),
						nodes.get(face[1] - 1), nodes.get(face[2] - 1));
				for (int i = 0; i < face.length; i++) {
					Node n = nodes.get(face[i] - 1);
					n.setComment(1);
				}
				faces.add(f);
			}
			face = stream.readFace();
		}
		ArrayList<Node> newNodes = new ArrayList<Node>();
		for (int i = 0; i < nodes.size(); i++) {
			Node n = nodes.get(i);
			if (n.getComment() == 1) {
				newNodes.add(n);
			}
		}
		nodes = newNodes;
		realBounds = new Bounds3D();
		realBounds.addAll(nodes);
		
		stream.close();
		stream = null;
	}

	public void draw() {
		if (flagCrop){
			cropToBounds(b3d);
			flagCrop=false;
		}
		if (flagSave){
			saveObjPrintwriter(pathObjIn+"_out.obj");
			flagSave=false;
		}
		if (flagRefresh) {
			setBounds();
			findBorders();
			println("draw");
			randomSeed(1);
			camera(800, 0, 0, 0, 0, 0, 0, -1, 0);
			lights();
			hint(ENABLE_DEPTH_TEST);
			pushMatrix();
			rectMode(CENTER);
			background(0);
			noStroke();
			fill(255);
			scale(200, 200, 200);
			rotateY(rotY);
			float tol = 0.03f;
			noFill();
			stroke(0, 0, 255);
			realBounds.display3D(g);
			noFill();
			stroke(0, 255, 255);
			b3d.display3D(g);
			fill(255, 0, 0);
			stroke(255, 0, 0);
			strokeWeight(2f);
			for (int i = 0; i < borderFaces.size(); i++) {
				SimpleTriangle tri = borderFaces.get(i);
				tri.display3D(g);
			}
			noStroke();
			fill(255);
			for (int i = 0; i < faces.size(); i++) {
				SimpleTriangle tri = faces.get(i);
				Box3D box = tri.getBounds3D();
				if (b3d.isOverlap(box)){
					tri.display3D(g);
				}
			}
			flagRefresh = false;
			popMatrix();
			hint(DISABLE_DEPTH_TEST);
		}

		gui();
	}

	public void findBorders() {
		borderFaces = new Vector<SimpleTriangle>();
		for (int i = 0; i < faces.size(); i++) {
			SimpleTriangle tri = faces.get(i);
			Box3D box = tri.getBounds3D();
			if (box.getX1() < b3d.getX1() && box.getX2() > b3d.getX1())
				borderFaces.add(tri);

			if (box.getX1() < b3d.getX2() && box.getX2() > b3d.getX2()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY1() && box.getY2() > b3d.getY1()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY2() && box.getY2() > b3d.getY2()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ1() && box.getZ2() > b3d.getZ1()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ2() && box.getZ2() > b3d.getZ2()) {
				borderFaces.add(tri);
			}
		}
	}
	public void saveObjPrintwriter(String file) {
		PrintWriter output = createWriter(file + ".obj");
		int idV = 1;
		for (int i = 0; i < faces.size(); i++) {
			SimpleTriangle f = faces.get(i);
			for (int j = 0; j < f.getNodesNum(); j++) {
				Vec3D n = (Vec3D) f.getNode(j);
				if (n.getId() == 0) {
					n.setId(idV);
					idV++;
					output.println("v " + n.x + " " + n.y + " " + n.z);
				}
			}
		}
		for (int i = 0; i < faces.size(); i++) {
			SimpleTriangle f = faces.get(i);
			String string = "f";
			for (int j = 0; j < f.getNodesNum(); j++) {
				Vec3D n = (Vec3D) f.getNode(j);
				
				string += " " + n.getId();
			}
			output.println(string);
		}
		output.flush(); // Writes the remaining data to the file
		output.close();
		println("saved");
	}
	
	public void gui() {
		stroke(0);
		fill(0);
		camera();
	}

	public void createGUI() {
		cp5 = new ControlP5(this);
		cp5.addSlider("minX").setPosition(20, 20).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("maxX").setPosition(20, 50).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("minY").setPosition(20, 80).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("maxY").setPosition(20, 110).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("minZ").setPosition(20, 140).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("maxZ").setPosition(20, 170).setRange(0, 100)
				.setSize(200, 20);
		;
		// cp5.setAutoDraw(false);

		RadioButton r = cp5.addRadioButton("radioButton").setPosition(20, 210)
				.setSize(10, 10).setColorForeground(color(120))
				.setColorActive(color(255)).setColorLabel(color(255))
				.setItemsPerRow(8).setSpacingColumn(20).addItem("50", 1)
				.addItem("100", 2).addItem("150", 3).addItem("200", 4)
				.addItem("250", 5).addItem("300", 6).addItem("350", 7)
				.addItem("400", 8)

		;
	}
	public void radioButton(int a) {
		flagRefresh = true;
		rotY = HALF_PI * a*0.5f;
		// println("a radio Button event: "+a);
	}
	public void keyPressed(){
		if (key=='c'){
			flagCrop=true;
		}
		if (key=='s'){
			flagSave=true;
		}
	}
}
